package com.wiki4j.gdo.impl;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.MapContext;

public class EL{
  public static Object evalQuiet(String expression,Map<String,Object> context){
    JexlEngine jexl=new JexlBuilder().create();
    Map<String,Object> map=new HashMap<String,Object>();
    if(context!=null) map.putAll(context);
    MapContext jc=new MapContext(map);
    JexlExpression engine=jexl.createExpression(expression);
    try{
      return engine.evaluate(jc);
    }catch(JexlException e){
      return null;
    }
  }

  public static Object eval(String expression,Map<String,Object> context){
    JexlEngine jexl=new JexlBuilder().create();
    Map<String,Object> map=new HashMap<String,Object>();
    if(context!=null) map.putAll(context);
    MapContext jc=new MapContext(map);
    JexlExpression engine=jexl.createExpression(expression);
    try{
      return engine.evaluate(jc);
    }catch(JexlException e){
      Throwable cause=e.getCause();
      if(cause!=null){
        if(cause instanceof IndexOutOfBoundsException) return null;
      }
      throw new IllegalArgumentException("Fail to eval: "+expression,e);
    }
  }

  public static String fix(String text,Map<String,Object> context){
    StringBuilder buffer=new StringBuilder(text);
    boolean hasQuote=parseQuote(buffer,context);
    if(hasQuote) return buffer.toString();
    return null;
  }

  public final static boolean parseQuote(StringBuilder buffer,Map<String,Object> context){
    return parseQuote(buffer,context,-1);
  }

  private final static boolean parseQuote(StringBuilder buffer,Map<String,Object> context,int start){
    boolean flag=false;
    int i=start;
    if(i<0) i=0;
    for(;i<buffer.length();i++){
      char c=buffer.charAt(i);
      switch(c){
        case '\\':
          if(buffer.length()>i+1){
            char next=buffer.charAt(i+1);
            if(next=='}') buffer.deleteCharAt(i);
            if(buffer.length()>i+2){
              char nextnext=buffer.charAt(i+2);
              if(next=='$'&&nextnext=='{'){
                buffer.deleteCharAt(i);
                i++;
              }
            }
          }
          break;
        case '$':
          if(buffer.length()>i+1){
            char next=buffer.charAt(i+1);
            if(next=='{'){
              int old=buffer.capacity();
              if(parseQuote(buffer,context,i+2)) flag=true;
              if(start>0) i-=buffer.capacity()-old;
            }
          }
          break;
        case '}':
          if(start>=0){
            String expression=buffer.substring(start,i);
            Object ro=eval(expression,context);
            String result=ro==null?"":ro.toString();
            if(result==null) result="";
            result=quoteRestore(result);
            buffer.replace(start-2,i+1,result);
            return true;
          }
          break;
      }
    }
    return flag;
  }

  public static final String quoteRestore(String src){
    if(src==null) return null;
    if(src.length()==0) return src;
    char[] cs=src.toCharArray();
    int index=0;
    for(int i=0;i<cs.length;i++){
      if(cs[i]=='\\'&&i<cs.length-1){
        char next=cs[i+1];
        if(next=='\\'||next=='$'||next=='{'||next=='}') i++;
      }
      cs[index++]=cs[i];
    }
    return String.valueOf(cs,0,index);
  }
}
